package aconcagua.measure

import java.util.List;

import groovy.lang.Closure;
import groovy.lang.Range;

class ArithmeticObjectInterval extends AbstractList implements Range {
	private start;
	private stop;
	private step;
	private size;
	private intervalStrategy;

	// (class) instance creation
	public ArithmeticObjectInterval(aStart, aStop, aStep) {
		constructorHelper(aStart, aStop, aStep, defaultIntervalStrategy());
	}

	public ArithmeticObjectInterval(aStart, aStop, aStep, anIntervalStrategy) {
		constructorHelper(aStart, aStop, aStep, anIntervalStrategy);
	}

	// instance creation - private
	private void constructorHelper(aStart, aStop, aStep, anIntervalStrategy) {
		assertIntervalIsValid(aStart, aStop, aStep, anIntervalStrategy);
		start = aStart;
		stop = aStop;
		step = aStep;
		intervalStrategy = anIntervalStrategy;
		size = calculateSize(start, stop, step, intervalStrategy);
	}
	
	// (class) instance creation assertions - private
	private static assertIntervalIsValid(aStart, aStop, aStep, anIntervalStrategy) {
		if(anIntervalStrategy.isInvalidInterval(aStart, aStop, aStep))
			throw new InvalidIntervalException(aStart, aStop, aStep);
	}

	// (class) accessing - private
	private defaultIntervalStrategy() {
		return new ArithmeticObjectIntervalStrategy();
	}
	
	private static calculateSize(aStart, aStop, aStep, anIntervalStrategy) {
		def steps = anIntervalStrategy.steps(aStart, aStop, aStep);
		return steps.toDouble().trunc() + 1;
	}

	// accessing
	public at(anIndex) {
		if (anIndex < 0 || anIndex > this.size())
			throw new IndexOutOfRangeException(anIndex, this);

		return intervalStrategy.advance(start, anIndex, step);
	}

	public start() {
		return start;
	}

	public stop() {
		return stop;
	}

	public step() {
		return step;
	}

	@Override
	public Object get(int index) {
		return this.at(index);
	}

	@Override
	public int size() {
		return size;
	}

	@Override
	public Comparable getFrom() {
		return this.start();
	}

	@Override
	public Comparable getTo() {
		return this.stop();
	}

	// comparing
	@Override
	public boolean equals(Object anInterval) {
		return (this.getClass() == anInterval.getClass()
			&& start == anInterval.start()
			&& step == anInterval.step()
			&& stop == anInterval.stop());
	}

	@Override
	public int hashCode() {
		return start.hashCode().xor(stop.hashCode().xor(step.hashCode()));
	}

	// copying
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return new ArithmeticObjectInterval(start, stop, step, intervalStrategy);
	}
	
	// enumerating
	@Override
	public void step(int aStep, Closure closure) {
		this.perform(aStep, aClosure);
	}
	
	public void perform(Closure aClosure) {
		this.perform(step, aClosure);
	}
	
	public void perform(Object aStep, Closure aClosure) {
		def element = start;
		aClosure.call(start);
		for (int i = 0; i < (size - 1); i++) {
			element = intervalStrategy.advance(element, aStep);
			aClosure.call(element);
		};
	}
	
	@Override
	public boolean isReverse() {
		// TODO Auto-generated method stub
		ObjectRange
		return false;
	}

	@Override
	public boolean containsWithinBounds(Object o) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public List step(int step) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String inspect() {
		// TODO Auto-generated method stub
		return null;
	}
}
